/*
 * Licensed Materials - Property of tenxcloud.com
 * (C) Copyright 2018 TenxCloud. All Rights Reserved.
 * 2018-06-13  @author lizhen
 */

package middleware

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"modules.tenxcloud.com/common/composite"
	"modules.tenxcloud.com/common/contract"
	"modules.tenxcloud.com/common/model/project"
	commonerr "modules.tenxcloud.com/common/utility/errors"
)

const (
	NamespaceKey        = "namespace"
	SystemAdmin         = 2
	PlatformAdmin       = 3
	InfrastructureAdmin = 4
)

func ValidateNamespace(c *gin.Context) {
	li, exist := c.Get(LoginUserKey)
	if !exist {
		glog.Fatal("ValidateNamespace middleware should work together with Authorization middleware")
	}
	u, _ := li.(contract.User)
	var ns *namespace
	for _, validator := range validators {
		var err error
		if ns, err = validator(c, u); err != nil {
			c.AbortWithStatusJSON(commonerr.Forbidden(err))
			return
		} else if ns != nil {
			break
		}
	}
	if ns == nil {
		ns = &namespace{
			spaceType: contract.UserSpace,
			space:     u.GetNamespace(),
		}
	}
	c.Set(NamespaceKey, ns)
	c.Next()
}

var (
	validators = []func(c *gin.Context, u contract.User) (*namespace, error){
		validateOnBehalfUser,
		validateProjectSpace,
	}
)

func forbidden(message string) *errors.StatusError {
	return &errors.StatusError{
		ErrStatus: metav1.Status{
			Status:  metav1.StatusFailure,
			Message: message,
			Reason:  metav1.StatusReasonForbidden,
			Code:    http.StatusForbidden,
		},
	}
}

func validateOnBehalfUser(c *gin.Context, u contract.User) (*namespace, error) {
	obu := c.Request.Header.Get("onbehalfuser")
	if obu == "" {
		return nil, nil
	}
	role := u.GetRole()
	if role != SystemAdmin && role != PlatformAdmin {
		return nil, forbidden("not admin")
	}
	return &namespace{
		spaceType: contract.OnBehalfUser,
		space:     obu,
	}, nil
}

func validateProjectSpace(c *gin.Context, u contract.User) (*namespace, error) {
	ps := c.Request.Header.Get("project")
	if ps == "" {
		if ps = c.Request.Header.Get("teamspace"); ps == "" {
			return nil, nil
		}
	}
	role := u.GetRole()
	ns := &namespace{
		spaceType: contract.ProjectSpace,
		space:     ps,
	}
	if role == SystemAdmin || role == InfrastructureAdmin {
		return ns, nil
	}
	db, err := composite.Database.GetOrm()
	if err != nil {
		return nil, err
	}
	if belongs, err := project.UserBelongsToProject(u.GetUserID(), ps, db); err != nil {
		return nil, err
	} else if !belongs {
		return nil, forbidden("not belongs to project")
	}
	return ns, nil
}

type namespace struct {
	spaceType contract.NamespaceType
	space     string
}

func (n namespace) Type() contract.NamespaceType {
	return n.spaceType
}

func (n namespace) Space() string {
	return n.space
}
